package com.ll.gulimall.serach.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.ll.common.constant.ElasticConstant;
import com.ll.common.to.es.SkuEsModel;
import com.ll.common.utils.R;
import com.ll.gulimall.serach.config.GulimallElasticSearchConfig;
import com.ll.gulimall.serach.feign.ProductFeignService;
import com.ll.gulimall.serach.service.MallSearchSearch;
import com.ll.gulimall.serach.vo.*;
import com.sun.deploy.security.AbstractBrowserAuthenticator;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.URIEditor;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

@Service
public class MallSearchSearchImpl implements MallSearchSearch {

    @Autowired
    RestHighLevelClient client;

    @Override
    public SearchResult search(SearchParam param) {
        SearchResult result = null;
        // 1、准备检索请求
        SearchRequest searchRequest = bulidSearchRequset(param);
        try {
            //2、执行检索请求
            SearchResponse response = client.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);

            // 3、分析响应数据
            result = bulidSearchResult(response, param);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    @Autowired
    ProductFeignService productFeignService;

    /**
     * 动态构造请求条件
     */
    private SearchRequest bulidSearchRequset(SearchParam param) {

        //构建DSL语句
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //构建查询DSL
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        // 1.1 根据keyword 检索商品skuTitle
        if (!StringUtils.isEmpty(param.getKeyword())) {
            queryBuilder.must(QueryBuilders.matchQuery("skuTitle", param.getKeyword()));
        }
        // 1.1 filter 根据brandId 过滤
        if (param.getBrandId() != null && param.getBrandId().size() > 0) {
            queryBuilder.filter(QueryBuilders.termsQuery("brandId", param.getBrandId()));

        }
        // 1.1 filter 根据catalogId过滤
        if (param.getCatalog3Id() != null) {
            queryBuilder.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));
        }
        // 1.1 filter 根据attrs过滤  attrs=1_5寸:6寸,attrs=2_晓龙:高通
        if (param.getAttrs() != null && param.getAttrs().size() > 0) {
            for (String attrStr : param.getAttrs()) {
                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                String[] s = attrStr.split("_");
                String attrId = s[0];//属性Id
                String[] attrValues = s[1].split(":");//属性值

                boolQuery.must(QueryBuilders.termQuery("attrs.attrId", attrId));
                boolQuery.must(QueryBuilders.termsQuery("attrs.attrValue", attrValues));
                queryBuilder.filter(QueryBuilders.nestedQuery("attrs", boolQuery, ScoreMode.None));
            }
        }
        // 1.1 filter 根据skuPrice 区间 过滤
        // 1_500 _500 500_  <=500 >=1
        if (!StringUtils.isEmpty(param.getSkuPrice())) {
            //开始拆分字符串
            String[] s = param.getSkuPrice().split("_");
            //区间query
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");
            if (s.length == 2) {
                // 输入了两个区间
                queryBuilder.filter(rangeQuery.gte(s[0]).lte(s[1]));
            } else if (s.length == 1) {
                //输入了一个区间
                if (param.getSkuPrice().startsWith("_")) {
                    // 没有输入最小值
                    queryBuilder.filter(rangeQuery.lte(s[0]));
                }
                if (param.getSkuPrice().endsWith("_")) {
                    // 没有输入最大值s
                    queryBuilder.filter(rangeQuery.gte(s[0]));
                }
            }
        }
        // 1.1 filter 根据hasStock过滤
        if (param.getHasSotck() != null) {
            queryBuilder.filter(QueryBuilders.termQuery("hasStock", param.getHasSotck() == 1));
        }
        // 1.2 分页   高亮
        /* 排序
         * sort=saleCount_asc/desc
         * sort=skuPrice_asc/desc
         * sort=hostSore_asc/desc
         * sort=
         */
        //排序
        if (!StringUtils.isEmpty(param.getSort())) {
            String[] s = param.getSort().split("_");
            if ("asc".equalsIgnoreCase(s[1])) {
                sourceBuilder.sort(s[0], SortOrder.ASC);
            }
            if ("desc".equalsIgnoreCase(s[1])) {
                System.out.println("desc");
                sourceBuilder.sort(s[0], SortOrder.DESC);
            }
        }
        //1.2 分页
        sourceBuilder.from((param.getPageNum() - 1) * ElasticConstant.PRODUCT_PAGESIZE);
        sourceBuilder.size(ElasticConstant.PRODUCT_PAGESIZE);
        //1.2 高亮

        if (!StringUtils.isEmpty(param.getKeyword())) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            sourceBuilder.highlighter(highlightBuilder);
        }
        // TODO 聚合品牌
        /*1.3 聚合 品牌 分类 属性 聚合分析
         */
        // 1.3品牌聚合
        AggregationBuilder aggBrand = AggregationBuilders.terms("brand_agg").field("brandId").size(100);
        // 品牌子聚合
        aggBrand.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
        aggBrand.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
        sourceBuilder.aggregation(aggBrand);

        // TODO 聚合分类
        // 1.3 分类聚合
        AggregationBuilder cataAgg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(1);
        cataAgg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
        sourceBuilder.aggregation(cataAgg);

        // TODO 聚合属性
        // 1.3 属性聚合
        AggregationBuilder arrtAgg = AggregationBuilders.nested("attrs_agg", "attrs");
        // 属性字聚合
        AggregationBuilder attrs_id_agg = AggregationBuilders.terms("attrs_id_agg").field("attrs.attrId").size(10);
        //给attrs_id_agg设置的子聚合
        attrs_id_agg.subAggregation(AggregationBuilders.terms("attrs_name_agg").field("attrs.attrName").size(10));
        attrs_id_agg.subAggregation(AggregationBuilders.terms("attrs_value_agg").field("attrs.attrValue").size(50));
        // 给attrAgg设置的子聚合
        arrtAgg.subAggregation(attrs_id_agg);
        sourceBuilder.aggregation(arrtAgg);

        // 把所有条件用来封装
        sourceBuilder.query(queryBuilder);
        System.out.println(sourceBuilder.toString());


        return new SearchRequest(new String[]{ElasticConstant.PRODUCT_INDEX}, sourceBuilder);
    }


    /**
     * 分析封装检索结果
     *
     * @param response
     * @return
     */
    private SearchResult bulidSearchResult(SearchResponse response, SearchParam param) {

        SearchResult result = new SearchResult();
        SearchHits hits = response.getHits();
        //1.获取所有商品信息 v
        List<SkuEsModel> skuEsModels = new ArrayList<>();
        if (hits.getHits() != null && hits.getHits().length > 0) {
            for (SearchHit hit : hits.getHits()) {
                String sourceAsString = hit.getSourceAsString();
                SkuEsModel skuEsModel = JSON.parseObject(sourceAsString, SkuEsModel.class);
                //设置高亮
                if (!StringUtils.isEmpty(param.getKeyword())) {
                    String skuTitle = hit.getHighlightFields().get("skuTitle").getFragments()[0].string();
                    skuEsModel.setSkuTitle(skuTitle);
                }
                skuEsModels.add(skuEsModel);
            }
            result.setProducts(skuEsModels);
        }
        //2.当前所有商品涉及到的所有属性信息 v
        List<SearchResult.AttrVo> attrVos = new ArrayList<>();
        ParsedNested attrsAgg = response.getAggregations().get("attrs_agg");
        List<? extends Terms.Bucket> attrsIdAgg = ((ParsedLongTerms) attrsAgg.getAggregations().get("attrs_id_agg")).getBuckets();
        if (attrsIdAgg != null && attrsIdAgg.size() > 0) {
            for (Terms.Bucket bucket : attrsIdAgg) {
                SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
                // 获取属性Id
                String attrId = bucket.getKeyAsString();
                attrVo.setAttrId(Long.parseLong(attrId));
                // 获取属性名称
                String attrName = ((ParsedStringTerms) bucket.getAggregations().get("attrs_name_agg")).getBuckets().get(0).getKeyAsString();
                attrVo.setAttrName(attrName);
                //获取属性值
                List<String> attrValues = new ArrayList<>();
                List<? extends Terms.Bucket> attrValue = ((ParsedStringTerms) bucket.getAggregations().get("attrs_value_agg")).getBuckets();
                for (Terms.Bucket valueBacket : attrValue) {
                    String keyAsString = valueBacket.getKeyAsString();
                    attrValues.add(keyAsString);
                }
                attrVo.setAttrValue(attrValues);
                attrVos.add(attrVo);
            }
            result.setAtts(attrVos);
        }

        //3.当前所有商品涉及到的所有品牌信息  v
        List<SearchResult.BrandVo> brandVos = new ArrayList<>();
        ParsedLongTerms brand_agg = response.getAggregations().get("brand_agg");
        for (Terms.Bucket bucket : brand_agg.getBuckets()) {
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            // 品牌Id
            String brandId = bucket.getKeyAsString();
            brandVo.setBrandId(Long.parseLong(brandId));
            // 品牌名称
            String brandName = ((ParsedStringTerms) bucket.getAggregations().get("brand_name_agg")).getBuckets().get(0).getKeyAsString();
            brandVo.setBrandName(brandName);
            // 品牌图片
            String brandImage = ((ParsedStringTerms) bucket.getAggregations().get("brand_img_agg")).getBuckets().get(0).getKeyAsString();
            brandVo.setBrandImg(brandImage);
            brandVos.add(brandVo);
        }
        result.setBrands(brandVos);
        //4、当前所有商品涉及到的所有分类信息 v
        List<SearchResult.catelogVo> catalogVos = new ArrayList<>();
        ParsedLongTerms catalog_agg = response.getAggregations().get("catalog_agg");
        List<? extends Terms.Bucket> buckets = catalog_agg.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            // 拿到分类id
            Long catalogId = Long.parseLong(bucket.getKeyAsString());
            //拿到分类名称
            String cataName = ((ParsedStringTerms) bucket.getAggregations().get("catalog_name_agg")).getBuckets().get(0).getKeyAsString();
            SearchResult.catelogVo catelogVo = new SearchResult.catelogVo();
            catelogVo.setCatalogId(catalogId);
            catelogVo.setCatalogName(cataName);
            catalogVos.add(catelogVo);
        }
        result.setCatelogs(catalogVos);
        //=======以上从聚合信息中获取=====
        //5.分页信息-页码 v
        result.setPageNum(param.getPageNum());
        //5.分页信息-总记录树 v
        long totalNum = hits.getTotalHits().value;
        //5、分页信息-总页码-计算 v
        result.setTotal(totalNum);
        result.setTotalPages(((int) totalNum % ElasticConstant.PRODUCT_PAGESIZE) == 0 ?
                ((int) totalNum / ElasticConstant.PRODUCT_PAGESIZE) :
                ((int) totalNum / ElasticConstant.PRODUCT_PAGESIZE + 1));

        //5、计算分页导航
        List<Integer> pageNavs = new ArrayList<>();
        for (Integer i = 1; i <= result.getTotalPages(); i++) {
            System.out.println(i);
            pageNavs.add(i);
        }

        result.setPageNavs(pageNavs);

        if (param.getBrandId() != null && param.getBrandId().size() > 0) {
            //设置导航项目
            R r = productFeignService.info(param.getBrandId());
            if (r.getCode() == 0) {//远程调用成功
                List<BrandEntity> brands = r.getData(new TypeReference<List<BrandEntity>>() {
                }, "brands");
                String url = "";
                for (int i = 0; i < brands.size(); i++) {
                    NavigationVo navigationVo = new NavigationVo();
                    navigationVo.setName("品牌");
                    navigationVo.setValue(brands.get(i).getName());
                    //&brandId=6&sort=hasStock_desc
                    String encode = "";
                    try {
                        encode = URLEncoder.encode(brands.get(i).getBrandId().toString(), "UTF-8");
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                    String queryString = param.getQueryString();
                    url = "http://search.gulimall.com/list.html?";
                    url = url + queryString.replace("&brandId=" + encode, "");
                    navigationVo.setLinkUrl(url);

                    result.getNavigationVos().add(navigationVo);
                }
            }
        }
        if (param.getAttrs() != null && param.getAttrs().size() > 0) {
            String url = "";
            for (String attrStr : param.getAttrs()) {
                StringBuffer buffer = new StringBuffer();
                String[] s = attrStr.split("_");
                String attrId = s[0];//属性Id
                result.getAttrIds().add(Long.parseLong(attrId));
                NavigationVo navigationVo = new NavigationVo();
                R r = productFeignService.info(Long.parseLong(attrId));
                buffer.append(attrId);
                if (r.getCode() == 0) {
                    AttrEntityTo attr = r.getData(new TypeReference<AttrEntityTo>() {
                    }, "attr");
                    navigationVo.setName(attr.getAttrName());//属性名称
                    navigationVo.setValue(s[1]);//属性值
                    //面包屑导航 &attrs=3_以官网信息为准 如果有空格需要转码
                    String encode = "";
                    try {
                        encode = URLEncoder.encode(attrStr, "UTF-8");
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                    String queryString = param.getQueryString().replace("&attrs=" + encode, "");
                    url = "http://search.gulimall.com/list.html?";
                    url = url + queryString;
                    navigationVo.setLinkUrl(url);
                    result.getNavigationVos().add(navigationVo);
                }

            }
        }
        if (param.getCatalog3Id() != null) {
            NavigationVo navigationVo = new NavigationVo();
            navigationVo.setName("分类");
            R r = productFeignService.infoFeign(param.getCatalog3Id());
            if (r.getCode() == 0) {
                CatalogTo data = r.getData(new TypeReference<CatalogTo>() {
                });
                navigationVo.setValue(data.getName());
                //&catalog3Id=225
                String url = "";
                String queryString = param.getQueryString().replace("&catalog3Id=" + param.getCatalog3Id(), "");
                url = "http://search.gulimall.com/list.html?";
                url = url + queryString;
                navigationVo.setLinkUrl(url);
                result.getNavigationVos().add(navigationVo);
            }


        }


        return result;
    }


}
